/******************************************************************************
 Copyright (c) 2010-2020 Qualcomm Technologies International, Ltd.
 All Rights Reserved.
 Qualcomm Technologies International, Ltd. Confidential and Proprietary.

 REVISION:      $Revision: #1 $
******************************************************************************/
#include "csr_synergy.h"

#include "csr_bt_gatt_private.h"

/* Check whether a result failed due to security -- and whether we can
 * and should do anything about this */
#ifndef EXCLUDE_CSR_BT_SC_MODULE
CsrBool CsrBtGattCheckSecurity(GattMainInst *inst,
                               att_result_t result,
                               CsrBtGattQueueElement *qelm)
{
#ifdef EXCLUDE_CSR_BT_GATT_GAP_MODULE
    CsrBtGattConfig    *ele;
#endif
    CsrBtGattConnFlags  flags = CSR_BT_GATT_FLAGS_NONE;

    /* We only deal with authentication and encryption. Authorisation
     * and key size must be dealt with in the application.
       If msgState == CSR_BT_GATT_MSG_QUEUE_CANCELLED the ungoing
       procedure is cancelled, e.g no need to start the security
       procedure. */
    if( qelm->securityFunc                                &&
        qelm->msgState != CSR_BT_GATT_MSG_QUEUE_CANCELLED &&
        (result == ATT_RESULT_INSUFFICIENT_AUTHENTICATION || 
         result == ATT_RESULT_INSUFFICIENT_ENCRYPTION) ) 
    {
        CsrBtGattConnElement *conn = NULL;
        
#ifndef EXCLUDE_CSR_BT_GATT_GAP_MODULE
        if (qelm->gattId != inst->privateGattId)
        {
            CSR_BT_GATT_CONN_INST_FIND_BT_CONN_GATT_ID(inst->connInst,
                                                       &conn,
                                                       qelm->btConnId,
                                                       qelm->gattId);
        }
        else
#endif /* !EXCLUDE_CSR_BT_GATT_GAP_MODULE */
        {
            conn = CSR_BT_GATT_CONN_INST_FIND_CONNECTED_CID(inst->connInst, 
                                                            &qelm->cid);
        }

        /* Is security allowed */
#ifndef EXCLUDE_CSR_BT_GATT_GAP_MODULE
        flags = conn->flags;
#else
        if (conn)
        {
            /* Check if the app has any preference on the security */
            ele = CSR_BT_GATT_CONFIG_FIND_BY_GATTID(conn->configList, &qelm->gattId);
            if (ele)
            {
                flags = ele->flags;
            }
        }
#endif
        if(conn && !(flags & CSR_BT_GATT_FLAGS_NO_AUTO_SECURITY))
        { /* raise security, core spec 10.3.2 (CSA3) */ 
            CsrUint16 securityRequirements;
            
            qelm->msgState = CSR_BT_GATT_MSG_QUEUE_IN_PROGRESS_SECURITY;

            if(conn->encrypted)
            {
                /* encrypted, but it is insufficient => use MITM protection */
                securityRequirements = DM_SM_SECURITY_AUTHENTICATED_BONDING;
                qelm->allowSecRaise = FALSE;
            }
            else
            {
                /* unencrypted => first try to use NO MITM protection */
                securityRequirements = DM_SM_SECURITY_UNAUTHENTICATED_BONDING;
                qelm->allowSecRaise = TRUE;
            }

            CsrBtScLeSecurityReqSend(CSR_BT_GATT_IFACEQUEUE,
                                     securityRequirements,
                                     conn->peerAddr,
                                     conn->l2capFlags);

            return TRUE;
        }
    }

    /* Security was not our concern... */
    return FALSE;
}

static void csrBtGattScLeSecurityCfmHandler(GattMainInst *inst, CsrBtScLeSecurityCfm *cfm)
{
    CsrBtGattConnElement *conn = CSR_BT_GATT_CONN_INST_FIND_FROM_ADDRESS(inst->connInst,
                                                                         CsrBtGattFindConnectedConnInstFromAddress,
                                                                         &(cfm->address)); 
    if (conn)
    {
        CsrBtGattQueueElement *qElem = CSR_BT_GATT_QUEUE_FIND_CID(inst->queue, &(conn->cid));

        if (qElem && qElem->gattMsg && qElem->securityFunc)
        {
            if ( qElem->securityFunc(inst, qElem, cfm->resultCode, cfm->resultSupplier) )
            { 
                if( (cfm->resultCode != CSR_BT_RESULT_CODE_SC_SUCCESS || cfm->resultSupplier != CSR_BT_SUPPLIER_SC )
                    || !qElem->allowSecRaise )
                {
                    /* If we failed to get the requested security or not allowed to raise security further,
                       set qElem>securityFunc to NULL to avoid retry */
                    qElem->securityFunc = NULL;
                }
                /* else, keep the security callback as we might try enabling security again with MITM authentication */
            }
            else
            { /* This procedure is finish. Start the next if any */
                CsrBtGattQueueRestoreHandler(inst, qElem);
            }
        }
        else
        {
            CsrGeneralException(CsrBtGattLto,
                                0,
                                CSR_BT_SC_PRIM,
                                CSR_BT_SC_LE_SECURITY_CFM,
                                0,
                                "CSR_BT_SC_LE_SECURITY_CFM received without GATT handler");
        }
    }
    /* Else - The connection has been release while running security, just ignore */
}

static void csrBtGattScAddressMappedIndHandler(GattMainInst *inst, CsrBtScLeAddressMappedInd *ind)
{
    CsrBtTypedAddr addr;
    CsrBtGattConnElement *conn;

    addr.addr = ind->randomAddr;
    addr.type = CSR_BT_ADDR_RANDOM;

    conn = CSR_BT_GATT_CONN_INST_FIND_FROM_ADDRESS(inst->connInst,
                                                   CsrBtGattFindConnectedConnInstFromAddress,
                                                   &addr);
    if (conn)
    {
        conn->idAddr = ind->idAddr;
    }
}


static void csrBtGattScSecurityEventIndHandler(GattMainInst *inst, CsrBtScSecurityEventInd *ind)
{
    if (ind->tpAddress.tp_type == CSR_BT_TRANSPORT_LE)
    {
        CsrBtGattConnElement *conn;

        conn = CSR_BT_GATT_CONN_INST_FIND_FROM_ADDRESS(inst->connInst,
                                                       CsrBtGattFindConnectedConnInstFromAddress,
                                                       &ind->tpAddress.addrt);
        if (conn)
        {
            switch (ind->event)
            {
                case CSR_BT_SC_SECURITY_EVENT_BOND:
                {
                    if (CsrBtTdDbDeviceExists(ind->tpAddress.addrt.type,
                                              &ind->tpAddress.addrt.addr))
                    {
                        CsrBtTdDbGattInfo info = { 0 };

                        CsrBtTdDbSetGattInfo(ind->tpAddress.addrt.type,
                                             &ind->tpAddress.addrt.addr,
                                             &info);
                    }

#ifdef CSR_BT_INSTALL_LE_PRIVACY_1P2_SUPPORT
                    if (CSR_BT_LE_LOCAL_FEATURE_SUPPORTED(inst->localLeFeatures,
                                                          CSR_BT_LE_FEATURE_LL_PRIVACY))
                    {
                        CsrBtGattReadByUuidPrivateHandler(inst,
                                                          CSR_BT_GATT_UUID_CENTRAL_ADDRESS_RESOLUTION_CHARAC,
                                                          conn->btConnId);
                    }
#endif
                    break;
                }

                default:
                    /* Ignore */
                    break;
            }
        }
    }
}

void CsrBtGattDispatchSc(GattMainInst *inst)
{
    CsrPrim type = *(CsrPrim *)inst->msg;
    switch(type)
    {
        case CSR_BT_SC_LE_SECURITY_CFM:
            csrBtGattScLeSecurityCfmHandler(inst,
                                            (CsrBtScLeSecurityCfm*)inst->msg);
            break;
        case CSR_BT_SC_LE_ADDRESS_MAPPED_IND:
            csrBtGattScAddressMappedIndHandler(inst,
                                               (CsrBtScLeAddressMappedInd*)inst->msg);
            break;
        case CSR_BT_SC_SET_EVENT_MASK_CFM:
            /* Received confirmation for Subscription of address mapped indication in SC*/
            break;
        case CSR_BT_SC_SECURITY_EVENT_IND:
            csrBtGattScSecurityEventIndHandler(inst,
                                               (CsrBtScSecurityEventInd*)inst->msg);
            break;
        default:
            CsrGeneralException(CsrBtGattLto,
                                0,
                                CSR_BT_SC_PRIM,
                                type,
                                0,
                                "GATT handler - unknown SC primitive");
            break;
    }
}

static CsrBool csrBtGattSecurityReqSecurityHandler(void            *gattInst, 
                                                   void            *qElem,
                                                   CsrBtResultCode result, 
                                                   CsrBtSupplier   supplier)
{
    CsrBtGattQueueElement *element = (CsrBtGattQueueElement *) qElem;
    CSR_UNUSED(gattInst);

    if (result == CSR_BT_RESULT_CODE_SC_SUCCESS && 
        supplier == CSR_BT_SUPPLIER_SC)
    { /* Security is set up with success. Map to the right  */
        result = CSR_BT_GATT_RESULT_SUCCESS;
        supplier = CSR_BT_SUPPLIER_GATT;
    }
    CsrBtGattStdBtConnIdCfmSend(CSR_BT_GATT_SECURITY_CFM,
                                element->gattId,
                                result,
                                supplier,
                                element->btConnId);
    /* Always returned FALSE to indicate that the security is finish.
       E.g. the csrBtGattScLeSecurityCfmHandler will call 
       CsrBtGattQueueRestoreHandler */
    return FALSE;
}

static void csrBtGattSecurityReqRestoreHandler(GattMainInst          *inst, 
                                               CsrBtGattQueueElement *element,
                                               CsrUint16  mtu)
{
    CSR_UNUSED(mtu);
    CsrBtResultCode resultCode     = CSR_BT_GATT_RESULT_UNACCEPTABLE_PARAMETER;
    CsrBtGattSecurityReq *prim     = element->gattMsg;

    if (prim->btConnId != CSR_BT_GATT_LOCAL_BT_CONN_ID)
    {
        CsrBtGattConnElement *conn = NULL;
        resultCode = CSR_BT_GATT_RESULT_UNKNOWN_CONN_ID;
        
#ifndef EXCLUDE_CSR_BT_GATT_GAP_MODULE
        CSR_BT_GATT_CONN_INST_FIND_BT_CONN_GATT_ID(inst->connInst,
                                                   &conn,
                                                   prim->btConnId,
                                                   prim->gattId);
#else
        conn = CSR_BT_GATT_CONN_INST_FIND_CONNECTED_CID(inst->connInst, 
                                                        &element->cid);
#endif /* !EXCLUDE_CSR_BT_GATT_GAP_MODULE */

        if (conn && CSR_BT_GATT_CONN_IS_CONNECTED(conn->state))
        {
            element->msgState = CSR_BT_GATT_MSG_QUEUE_IN_PROGRESS_SECURITY;
            CsrBtScLeSecurityReqSend(CSR_BT_GATT_IFACEQUEUE,
                                       prim->securityRequirements, 
                                       conn->peerAddr,
                                       conn->l2capFlags); 
            return;
        }
    }
    CsrBtGattStdBtConnIdCfmSend(CSR_BT_GATT_SECURITY_CFM,
                                prim->gattId,
                                resultCode,
                                CSR_BT_SUPPLIER_GATT,
                                prim->btConnId);

    /* This procedure has finished. Start the next if any */
    CsrBtGattQueueRestoreHandler(inst, element);
}

CsrBool CsrBtGattSecurityReqHandler(GattMainInst *inst)
{
    CsrBool tmp;
    CsrBtGattSecurityReq *prim = (CsrBtGattSecurityReq *) inst->msg;

    (void)(CSR_BT_GATT_CONN_INST_FIND_BTCONN_ID_FROM_ID_MASK(inst->connInst, &prim->btConnId));

    tmp = CsrBtGattNewReqHandler(inst,
                                 inst->msg,   
                                 prim->btConnId, 
                                 prim->gattId,
                                 csrBtGattSecurityReqRestoreHandler,
                                 NULL,
                                 csrBtGattSecurityReqSecurityHandler);
    inst->msg = NULL;
    return tmp;
}
#endif /* !EXCLUDE_CSR_BT_SC_MODULE */

